package dk.dtu.is.jjmvcp.controller;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JButton;
import javax.swing.JToggleButton;

import dk.dtu.is.jjmvcp.model.MyJavaBeanModel;
import dk.dtu.is.jjmvcp.service.RandomService;
import dk.dtu.is.jjmvcp.service.iService;
import dk.dtu.is.jjmvcp.view.MyView;

/**
 * This is the Controller class of the MVC pattern.</br>
 * The Controller has both the Model and the View hardwired in. </br></br>
 * 
 * The controller is also the default listener for whatever happens in the <i>view</i>, i.e. in the {@link MyView MyView} </br>
 * In particular, it listens for the button click, and acts consequently. </br></br>
 * 
 * The controller is also the {@link Observer Observer} of any {@link iService iService}, in this case, of the {@link RandomService RandomService}.
 * This controller reacts to notifications from the remote service and take consequent proper actions for updating the <i>view</i> and the <i>model</i>.
 *  
 * @author dsac
 */
public class MyController implements ActionListener, Observer {

	//---------------
	// IMPORTANT: here we add the reference to the model and to the view
	//---------------
	/**
	 * The references to the model 
	 */
	private MyJavaBeanModel myJavaBeanModel;
	
	/**
	 * The references to the view 
	 */
	private MyView myView;
	
	
	/**
	 * The remote service from which we retrieve data for the model, i.e. for the Java Bean
	 * In this example, we simply create a local random service
	 */
	private RandomService randomService;
	
	// flag which signals whether the remote service is being polled or not.
	private boolean isPolling = false;
	
	/**
	 * Default constructor.
	 * <p>
	 * In the constructor the remote service is being initialized. </br>
	 * </p><p>
	 * The <i>Observer Pattern</i> is used to manage the notifications between the 
	 * remote service (that is, the {@link #randomService randomService}) and the 
	 * current controller.
	 * </p><p>
	 * The key method in this context is: <blockquote><tt>
     * randomService.addObserver(this);</tt></blockquote> 
     * through which the current controller is added to the list of observers of the 
     * remote service.  </br>
     * Doing so, when the remote service notifies that <i>something</i> has changed, the controller
     * will run the {@link #update(Observable, Object) update(Observable, Object)} method. </br>
     * That <i>something</i> occurs every time to the method {@link RandomService#notifyObservers()} 
     * is being called within a class that has extended the {@link Observable Observable} class.
	 * </p><p>
	 * In this prototype example, {@link #randomService randomService} extends the {@link Observable Observable} class.
	 * </p>
	 */
	public MyController() { 
		randomService = new RandomService();
		randomService.addObserver(this);
		randomService.setPollingPeriod(5000);
	}
	
	
	/**
	 * Adds the model to this controller.
	 *
	 * @param myJavaBeanModel the current data model to be added
	 */
	public void addModel(MyJavaBeanModel myJavaBeanModel) {
		if (myView != null)
		{
			myView.addLogMessage("[Controller] - Adding Model - that is, adding MyJavaBeanModel");
		}
		this.myJavaBeanModel = myJavaBeanModel;
	}
	
	/**
	 * Adds the view to this controller.
	 *
	 * @param myView the current view to be added
	 */
	public void addView(MyView myView) {
		if (myView != null)
		{
			myView.addLogMessage("[Controller] - Adding View - that is, adding this panel");
		}
		this.myView = myView;
	}

	/**
	 * Detect the change in the view when the buttons are pressed and act consequently.
	 * In this example, the value of the MyJavaBeanModel gets updated by the Service Layer.
	 * 
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	@Override
	public void actionPerformed(ActionEvent e) 
	{
		/*
		 * distinguish the source of the event.
		 * 
		 * 1) If it is the JToggleButton, it means that the user wants to start/stop the poller.
		 * In this case I use the variable "isPolling" as a flag to detect whether the remote 
		 * service is already polling (i.e. the toggle button is pressed) or whether the remote
		 * service is not polling (i.e. the toggle button is not pressed)
		 * 
		 * 2) If it is the JButton, the user wants to retrieve the bean only once.
		 */
		if (e.getSource() instanceof JToggleButton)
		{
			if (isPolling) 
			{
				// update the view with a message
				myView.addLogMessage("[Controller] - View Event: ActionEvent - INIT - Stopping the poller...");
			
				// send the request to the service layer 
				randomService.stopPolling();
				isPolling = false;
				
				// update the view with a message
				myView.addLogMessage("[Controller] - View Event: ActionEvent - END");
			} else 
			{
				// update the view with a message
				myView.addLogMessage("[Controller] - View Event: ActionEvent - INIT - Starting the poller...");
			
				// send the request to the service layer 
				randomService.startPolling();
				isPolling = true;
				
				// update the view with a message
				myView.addLogMessage("[Controller] - View Event: ActionEvent - END");
			}
		}
		
		if (e.getSource() instanceof JButton)
		{
			isPolling = false;
			
			// update the view with a message
			myView.addLogMessage("[Controller] - View Event: ActionEvent - INIT - Retrieving new MyJavaBeanModel value...");
		
			// send the request to the service layer 
			// retrieve the bean only once.
			randomService.getMyJavaBean();
			
			// update the view with a message
			myView.addLogMessage("[Controller] - View Event: ActionEvent - END");
		}
	}


	/**
	 * 
	 * This method is automatically called when the {@link #randomService randomService} notifies its observers
	 * by invoking the method {@link Observable#notifyObservers()}.
	 * <p>
	 * Interestingly enough, this method {@link #update(Observable, Object)} accepts arguments, thus allowing the observable object to pass values/objects
	 * to the observer. In this way, a communication channel is being created between the observers and the observable object.
	 * </p> 
	 * <p>
	 * In this prototype, the new bean which has been retrieved by the {@link MyController#randomService} is passed in the 
	 * {@code arg} argument of the method. This allows to pass the same object from the observable object to the observer.
	 * </p>
	 * <p>
	 * Another way for passing values between the observable object and the observer object can also be implemented.
	 * For example, the observer could invoke a specific ah-doc public method of the observable object. Such method 
	 * would expose relevant data, which would then be returned to the observer. </br>
	 * </p>
	 * 
	 * @param o the Observable object which triggered this method to be invoked. In this case, it is the {@link #randomService}.
	 * @param arg any other object that are relevant to be passed from the observable object to the observer. 
	 * In this implementation, the {@link MyJavaBeanModel MyJavaBeanModel} object that has been retrieved by the {@link #randomService}
	 * has been added as {@code arg}. Thus {@code arg} is an object of type {@link MyJavaBeanModel}. 
	 * This is set in the call {@link RandomService#notifyObservers(Object)} in the {@link #randomService}.
	 * 
	 * @see RandomService#notifyObservers(Object)
	 * @see RandomService#getMyJavaBeanAndNotify()
	 * @see Observable
	 * @see Observer
	 */
	@Override
	public void update(Observable o, Object arg) {

		// I have only one observer, and I know that the only object we pass is the bean
		MyJavaBeanModel newMyJavaBeanModel = (MyJavaBeanModel) arg;
		
		// update the view with a message
		myView.addLogMessage("   [Controller] - ServiceLayer Event - INIT - I got the Service Layer response. I update the model now...");
		
		// update the current instance of the JavaBean by copying the important properties in it
		// this will trigger the property change event for each property being updated.
		this.myJavaBeanModel.copyValuesFrom(newMyJavaBeanModel);
		
		// update the view with a message
		myView.addLogMessage("   [Controller] - ServiceLayer Event - END");
				
	}
}
